﻿#pragma once
#include "PreInclude.h"

/**
 * Sln_T : 问题的接口类型
 * CreateSolutions() 利用可变模板参数转入多个Sln_T的实例
 */
template<typename Sln_T>
class SolutionBuilder
{
public:
	SolutionBuilder() = default;

	template<typename... Slns_T>
	SolutionBuilder(Slns_T... slns)
	{
		CreateSolutions(slns...);
	}

	virtual ~SolutionBuilder()
	{
		STLUtil::ClearVector(_solutions);
	}

	template<typename FirstSln_T, typename... LeftSlns_T>
	void CreateSolutions(FirstSln_T* first, LeftSlns_T... lefts)
	{
		static_assert(std::is_base_of_v<Sln_T, FirstSln_T>);

		_solutions.emplace_back(first);
		CreateSolutions(lefts...);
	}

	template<typename FirstSln_T>
	void CreateSolutions(FirstSln_T* first)
	{
		static_assert(std::is_base_of_v<Sln_T, FirstSln_T>);

		_solutions.emplace_back(first);
		this->OnSolutionsCreated(&_solutions);
	}

	virtual std::vector<Sln_T*> GetSlns() const { return _solutions; }
protected:
	virtual void OnSolutionsCreated(const void* slns) {}
	virtual void OnSolutionsClear(){}
private:
	std::vector<Sln_T*> _solutions;
};

#ifdef DEV_TEST
template<typename Solution_T, typename BaseTest_T = testing::Test>
class SolutionTestor :public BaseTest_T
{
public:
	SolutionTestor(testing::Test* gTest = nullptr)
	{
		if (gTest != nullptr)
			this->_gTest = gTest;
		else
		{
			//尝试将 this 转换为 gTest
			try
			{
				this->_gTest = dynamic_cast<testing::Test*>(const_cast<SolutionTestor*>(this));
			}
			catch (...)
			{
				this->_gTest = nullptr;
			}
		}
	}
protected:
	typedef Solution_T SolutionType;
	typedef BaseTest_T BaseTestType;
protected:
	//实现自 testing::Test.SetUp()
	virtual void SetUp()
	{
		this->LoadSolutions(_solutions);
	}
	//实现自 testing::Test.TearDown()
	virtual void TearDown()
	{
		STLUtil::ClearVector(_solutions);
	}
	/**
	 * 装载问题所有的solutions方法
	 * 该函数会在每个测试例开始前调用一次
	 */
	virtual void LoadSolutions(std::vector<Solution_T*>& solutions)
	{
	}

	//SolutionBuilder的通知重写
	virtual void OnSolutionsCreated(const void* slns)
	{
		if (!slns)
			return;

		_solutions = *static_cast<const std::vector<Solution_T*>*>(slns);
	}
	virtual void OnSolutionsClear()
	{
		_solutions.clear();
	}
public:
	/**
	 * 在子类TestBody()中调用,为每种solution在触发给定的回调测试函数
	 */
	template<typename T>
	void TestForSolutions(T testBodyToInvoke)
	{
		this->TestForSolutions(testBodyToInvoke, this->_solutions);
	}
	template<typename T>
	void TestForSolutions(T testBodyToInvoke, const std::vector<Solution_T*>& slns)
	{
		DEV_ASSERT(!slns.empty()) << "load solutions in LoadSolutions() firstly";

		if (_testingModeRunOrders.empty())
			_testingModeRunOrders.emplace_back("default_mode");

		_inTestingInfo.Clear();
		_inTestingInfo.TestEnv = _gTest;

		for (auto& solution : slns)
		{
			_inTestingInfo.Solution = solution;
			++_inTestingInfo.SolutionIndex;

			std::unordered_set<std::string> used_modes;
			while (used_modes.size() < _testingModeRunOrders.size())
			{
				for (std::string& testing_mode : _testingModeRunOrders)
				{
					if (testing_mode.empty() || used_modes.count(testing_mode))
						continue;
					_inTestingInfo.TestingMode = testing_mode;

					TestForSolutionInMode(solution, _inTestingInfo.TestingMode, testBodyToInvoke);
					used_modes.insert(_inTestingInfo.TestingMode);
					break;
				}
			}
		}
	}

	//当一个solution实例正在进行测试时的信息
	struct SolutionInTestingInfo
	{
	public:
		bool IsInTesting() const { return _isInTesting; }
		void BeginTest()
		{
			_isInTesting = true;

			std::cout << "[Solution Test] "<<"solution:"<<SolutionIndex
					  <<" mode:" << TestingMode
					  <<" test_times:" << CurrentTestTimes << "/" << TotalTestTimes << std::endl;
		}
		void EndTest()
		{
			_isInTesting = false;
		}
		
		//测试时使用的模式
		std::string TestingMode = "";
		//在google test的哪个TestBody()中进行测试？
		testing::Test* TestEnv = nullptr;
		//进行测试的方案实例
		SolutionType* Solution = nullptr;
		//进行测试方案所在的顺序索引位置
		int SolutionIndex = -1;
		//当前进行到的测试次数
		int CurrentTestTimes = 0;
		//总共要执行的测试次数
		int TotalTestTimes = 0;
		//测试过程中是否因为出现错误而中断进行？
		bool HasFailureBreak = false;

		void Clear()
		{
			_isInTesting = false;
			TestingMode = "";
			TestEnv = nullptr;
			Solution = nullptr;
			SolutionIndex = -1;
			CurrentTestTimes = 0;
			TotalTestTimes = 0;
			HasFailureBreak = false;
		}
	private:
		//当前是否处于测试进行的状态？
		bool _isInTesting = false;
	};
	const SolutionInTestingInfo& getInTestingInfo() const { return _inTestingInfo; }
private:
	template<typename T>
	void TestForSolutionInMode(Solution_T* solution, const std::string& testing_mode, T testBodyToInvoke)
	{
		if (!solution || testing_mode.empty())
			return;
		
		int test_times = std::max<int>(1, getTestingModeSetting<int>("test_times", testing_mode));
		_inTestingInfo.TotalTestTimes = test_times;
		_inTestingInfo.CurrentTestTimes = 0;
		do
		{
			++_inTestingInfo.CurrentTestTimes;
			
			_inTestingInfo.BeginTest();
			{
				//第一次的TestBody固定要触发，在第一次的TestBody内可能会设置testing mode setting
				testBodyToInvoke(solution);
				if (_gTest && _gTest->HasFailure())
				{
					_inTestingInfo.HasFailureBreak = true;
					_inTestingInfo.EndTest();
					return;
				}
			}
			_inTestingInfo.EndTest();
		} while (_inTestingInfo.CurrentTestTimes<_inTestingInfo.TotalTestTimes);
	}
public:
	//当设置一个测试方法回调后，开始为每个solution进行测试调用
	SolutionTestor<Solution_T, BaseTest_T>& operator=(std::function<void(Solution_T*)> testBody)
	{
		TestForSolutions(testBody);
		return *this;
	}

	//设置要为一个solution重复进行的测试次数
	void setTestTimes(int testTimes, const std::string& testing_mode = "default_mode")
	{
		if (testing_mode.empty())
		{
			LOG << LL_ERROR << "testing_mode should not be empty!" << LOG_LOCALE << LS_ENDL;
			return;
		}
		setTestingModeSetting("test_times", testTimes, testing_mode);
	}

	template<typename T>
	void setTestingModeSetting(const std::string& setting_key, const T& setting_val, const std::string& testing_mode = "default_mode")
	{
		if (setting_key.empty() || testing_mode.empty())
			return;
		if (_testingModeSettings.count(testing_mode) == 0)
		{
			if(std::find(_testingModeRunOrders.begin(),_testingModeRunOrders.end(),testing_mode)==_testingModeRunOrders.end())
				_testingModeRunOrders.push_back(testing_mode);
			_testingModeSettings[testing_mode] = TestingModeSettings();
		}
		TestingModeSettings& settings = _testingModeSettings[testing_mode];
		settings[setting_key] = StringUtil::ToString(setting_val);
	}

	//获取指定testing_mode下一项配置值，如果testing_mode或setting_key不存在，则返回默认值
	template<typename T = std::string>
	T getTestingModeSetting(const std::string& setting_key, const std::string& testing_mode = "default_mode")
	{
		if (testing_mode.empty() || setting_key.empty() || _testingModeSettings.count(testing_mode) == 0)
			return T{};
		auto& settings = _testingModeSettings[testing_mode];
		if (settings.count(setting_key) == 0)
			return T{};

		return StringUtil::ParseString<T>(settings[setting_key]);
	}
private:
	//同一个类型问题的不同解决方案
	std::vector<Solution_T*> _solutions;

	//谷歌测试的调用实例，如果自身类继承的基类中有testing::Test,则_gTest指向自己
	//如果 自身类在声明定义的地方是 TEST()方法内，则_gTest指向TEST()方法的testing::Test
	testing::Test* _gTest;

	//按顺序执行的TestingMode
	std::vector<std::string> _testingModeRunOrders;
	typedef std::unordered_map<std::string, std::string> TestingModeSettings;
	//每种TestingMode对应的配置数据
	std::unordered_map<std::string, TestingModeSettings> _testingModeSettings;

	//正在进行测试的信息
	SolutionInTestingInfo _inTestingInfo;
};
#endif

#define TEST_SOLUTION1(ISln,Sln1,...)\
	SolutionTestor<ISln, SolutionBuilder<ISln>> __slnTestor(this);\
    __slnTestor.CreateSolutions(new Sln1);\
	__slnTestor=[__VA_ARGS__](ISln* solution)

#define TEST_SOLUTION2(ISln,Sln1,Sln2,...)\
	SolutionTestor<ISln, SolutionBuilder<ISln>> __slnTestor(this);\
    __slnTestor.CreateSolutions(new Sln1,new Sln2);\
	__slnTestor =[__VA_ARGS__](ISln* solution)

#define TEST_SOLUTION3(ISln,Sln1,Sln2,Sln3,...)\
	SolutionTestor<ISln, SolutionBuilder<ISln>> __slnTestor(this);\
    __slnTestor.CreateSolutions(new Sln1,new Sln2,new Sln3);\
	__slnTestor=[__VA_ARGS__](ISln* solution)